from google.appengine.ext import ndb
from datamodel import Account, Article, Reply
import sys

def YearMonth(date, add): # only alowed +1 or -1
    year, month = date[0], date[1]
    month += add
    if month == 0:
        month = 12
        year -= 1
    elif month == 13:
        month = 1
        year += 1
    return (year, month)

def OrReplyKey(model, date, article_id=None):
    if article_id is None: # get article key
        return ndb.Key(model, (date[0]*100+date[1])*100000000000L)
    else: # get reply key
        return ndb.Key(Article, article_id, Reply, (date[0]*100+date[1])*100000000000L)

# +1: 2013.4 -> [2013.4, 2013.5) [2013.5, 2013.6)
# -1: 2013.4 -> [2013.4, 2013.5) [2013.3, 2013.4)
# pay attention to article_id, not use None
def MonthCombine(model, qry, date, direction=+1, article_id=None, maximum=50):
    date_first = date_last = (None, None)
    date_cur = date
    count = 0
    is_cur_end = True
    is_next_end= False
    while True:
        n = qry.filter(model.key > OrReplyKey(model, date_cur, article_id))\
            .filter(model.key < OrReplyKey(model, YearMonth(date_cur, +1), article_id)).count()
        if n == 0:
            date_cur = YearMonth(date_cur, direction) # year, month + direction
            if direction == +1:
                n = qry.filter(model.key > OrReplyKey(model, date_cur, article_id)).count()
            else:
                n = qry.filter(model.key < OrReplyKey(model, YearMonth(date_cur, +1), article_id)).count()
            if n == 0:
                is_next_end = True
                break # return if end
            else:
                continue

        if n > maximum:
            if is_cur_end is False: # drop
                break

            # first non zero month > maximum
            is_cur_end = False
            date_first = date_last = date_cur
            date_cur = YearMonth(date_cur, direction) # WARN: y_cur, m_cur force forward
            break # return

        if n + count > maximum: # WARN: y_cur, m_cur not rollback
            break

        if is_cur_end: # only first time
            is_cur_end = False
            date_first = date_cur
        date_last = date_cur
        count += n
        date_cur = YearMonth(date_cur, direction)

    return (is_cur_end, date_first, date_last, is_next_end, date_cur)

# return [((2013,8),(2013,7)), ((2013,6),(2013,6)), ...]
def MonthList(model, qry, date_new, date_old, article_id=None, pad=2):
    cursor = 0
    date_list = []
    is_end = False
    date_first = date_last = (None, None)
    # get newer
    is_current = False
    date_cur = YearMonth(date_new, +1)
    for i in reversed(range(pad)): # 1,0
        is_cur_end, date_first, date_last, is_next_end, date_cur = MonthCombine(model, qry, date_cur, +1, article_id)
        if is_cur_end is False:
            date_list.insert(0, (date_last, date_first, is_current))
        else:
            pad = pad + i + 1
            break
        if is_next_end is True:
            pad += i
            break

    #
    if date_old != date_new:
        pad -= 1
        is_current = False
        date_list.append((date_new, date_old, True))
        date_cur = YearMonth(date_old, -1)
    else:
        is_current = True
        date_cur = date_old

    # get curent and older or older only
    for i in range(pad + 1): # 0,1,2
        is_cur_end, date_first, date_last, is_next_end, date_cur = MonthCombine(model, qry, date_cur, -1, article_id)
        if is_cur_end is False:
            date_list.append((date_first, date_last, is_current))
            is_current = False
        if is_next_end is True:
            break

    return date_list
